package FlowGraph.SSA;

import java.util.*;
import java.io.*;

import antlr.collections.AST;

import SymbolTable.*;
import JavaGrammar.*;
import db.ClassData;

//////////////////////////////////////////////////////////////////////////
//// VariableInfo
/**

@author Ivan Jeukens
@version v 0.1 31/01/2003
*/
public class VariableInfo {

    public VariableInfo(STEntry entry, ClassData cinfo) {
        _class = cinfo;
        _entry = entry;

        _useSites = new LinkedList();
        _defSites = new LinkedList();

        // Usado pelo algoritmo de renaming
        _count = 0;
        _stack = new Stack();
        _stack.push(new Integer(0));

        _defChain = new Vector();
        _useChain = new Vector();

        _useChain.setSize(1);
        _useChain.setElementAt(new LinkedList(), 0);

        _defChain.setSize(1);
        _defChain.setElementAt(new LinkedList(), 0);
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public  methods                    ////

    public ClassData getClassInfo() {
        return _class;
    }

    public STEntry getSTEntry() {
        return _entry;
    }

    public void addUseSite(VariableLocation loc) {
        _useSites.add(loc);
    }

    public Iterator useSitesIterator() {
        return _useSites.iterator();
    }

    public void removeUseSite(VariableLocation loc) {
        _useSites.remove(loc);
    }
    
    public void addDefSite(VariableLocation loc) {
        _defSites.add(loc);
    }

    public Iterator defSitesIterator() {
        return _defSites.iterator();
    }

    public void removeDefSite(VariableLocation loc) {
        _defSites.remove(loc);
    }

    public void debug(DataOutputStream dout) {
        try {
            dout.writeUTF(_entry.toString());
            dout.writeInt(_defSites.size());
            for(int i = 0;i < _defSites.size();i++) {
                VariableLocation loc = (VariableLocation) _defSites.get(i);
                dout.writeInt(loc.getFGNode().hashCode());
                if(loc.getASTNode() == null) {
                    dout.writeUTF("null");
                }
                else {
                    LinkedAST last = (LinkedAST) loc.getASTNode();
                    dout.writeUTF(last.getParent().toStringTree());
                }
            }
            dout.writeInt(_useSites.size());
            for(int i = 0;i < _useSites.size();i++) {
                VariableLocation loc = (VariableLocation) _useSites.get(i);
                dout.writeInt(loc.getFGNode().hashCode());
                if(loc.getASTNode() == null) {
                    dout.writeUTF("null");
                }
                else {
                    LinkedAST last = (LinkedAST) loc.getASTNode();
                    dout.writeUTF(last.getParent().toStringTree());
                }
            }

            // Salva as cadeias no formato DEF_i,USE_i...USE_i,DEF_i+1,
            // USE_i+1,....USE_i+1,DEF_i+2....etc

            //System.out.println("Chains for: " + _entry);
            dout.writeInt(_defChain.size());
            for(int i = 0;i < _defChain.size();i++) {
                //System.out.println("Index = " + i);
                LinkedList l = (LinkedList) _defChain.get(i);

                LinkedList l2 = null;
                if(i < _useChain.size()) {
                    l2 = (LinkedList) _useChain.get(i);
                }

                if(l2 != null) {
                    dout.writeInt(l.size() + l2.size());
                }
                else {
                    dout.writeInt(l.size());
                }
                //Primeiro salva as info dos defs
                for(int j = 0;j < l.size();j++) {
                    VariableLocation loc = (VariableLocation) l.get(j);
                    dout.writeInt(loc.getFGNode().hashCode());
                    dout.writeBoolean(true);

                    //if(j == 0) {
                    //    System.out.print("Def : ");
                    //}
                    //else {
                    //    System.out.print("Use : ");
                    //}
                    
                    if(loc.getASTNode() == null) {
                        dout.writeUTF("SOURCE-MethodVar");
                        //System.out.println("SOURCE-MethodVar\n");
                    }
                    else {
                        LinkedAST last = (LinkedAST) loc.getASTNode();
                        dout.writeUTF(last.getParent().toStringTree());
                        //System.out.println(last.getParent().toStringTree()
                        //    + "\n");
                    }
                }

                //System.out.println("l2 = "+ l2);
                if(l2 != null) {
                    for(int j = 0;j < l2.size();j++) {
                        VariableLocation loc = (VariableLocation) l2.get(j);
                        dout.writeInt(loc.getFGNode().hashCode());
                        dout.writeBoolean(false);

                        //if(j == 0) {
                        //    System.out.print("Def : ");
                       // }
                        //else {
                         //   System.out.print("Use : ");
                        //}
                        
                        if(loc.getASTNode() == null) {
                            dout.writeUTF("null!!!");
                            //System.out.println("null\n");
                        }
                        else {
                            LinkedAST last = (LinkedAST) loc.getASTNode();
                            dout.writeUTF(last.getParent().toStringTree());
                            //System.out.println(last.getParent().toStringTree()
                            //   + "\n");
                        }
                    }
                }
            }
            //System.out.print("\n");
        }
        catch(IOException ex) {
            System.out.println(ex);
        }
    }

// Usado durante o algoritmo de rename do SSA
    public int getCount() {
        return _count;
    }

    public void incCount() {
        _count++;
    }

    public int getTopStack() {
        Integer i = (Integer) _stack.peek();
        return i.intValue();
    }

    public Object popStack() {
        return _stack.pop();
    }

    public void pushStack(int i) {
        Integer iv = new Integer(i);
        _stack.push(iv);
    }

    public void addUseToChain(int index, VariableLocation loc) {
        //System.out.println("Adicionando uso index = " + index);
        //System.out.println("Var : " + _entry.getName());

        // System.out.println("AST : " + loc.getASTNode().toStringTree());
        //System.out.println("Use Chain size = " + _useChain.size()); 

        LinkedList l;
        if(_useChain.size() < index + 1) {
            l = new LinkedList();
            _useChain.setSize(index + 1);
            _useChain.setElementAt(l, index);
        }
        else {
            l = (LinkedList) _useChain.get(index);
            // pode ser null devido a back edges
            if(l == null) {
                l = new LinkedList();
                _useChain.setElementAt(l, index);
            }
        }

        /*System.out.println("L antes");
        for(int i = 0;i < l.size();i++) {
            loc = (VariableLocation) l.get(i);
            System.out.println("loc -> " + loc.getASTNode().toStringTree());
        }*/
        //pode colocar na ultima posicao
        l.addLast(loc);
        /*System.out.println("L depois");
        for(int i = 0;i < l.size();i++) {
            loc = (VariableLocation) l.get(i);
            System.out.println("loc -> " + loc.getASTNode().toStringTree());
        }*/
    }

    public void addDefToChain(int index, VariableLocation loc) {
        //System.out.println("Adicionando def index = " + index);
        //System.out.println("Var : " + _entry.getName());
        /*
        System.out.println("AST : " + loc.getASTNode().toStringTree());
        System.out.println("Def Chain size = " + _defChain.size());*/

        LinkedList l;
        if(_defChain.size() < index + 1) {
            l = new LinkedList();
            _defChain.setSize(index + 1);
            _defChain.setElementAt(l, index);
        }
        else {
            l = (LinkedList) _defChain.get(index);
            if(l == null) {
                l = new LinkedList();
                _defChain.setElementAt(l, index);
            }
        }

        //System.out.println("L antes : " + l);
        // pode colocar na ultima posicao
        l.addFirst(loc);
        //System.out.println("L depois : " + l);
    }

    public String toString() {
        return _entry.getName();
    }

    public int defChainSize() {
        return _defChain.size();
    }
    
    public LinkedList getDefFromChain(int index) {
        if(index < 0 || index >= _defChain.size()) return null;
        return (LinkedList) _defChain.get(index);
    }

    public LinkedList getUseFromChain(int index) {
        if(index < 0 || index >= _useChain.size()) return null;
        return (LinkedList) _useChain.get(index);
    }

    public void removeIndex(int index) {
        LinkedList l = (LinkedList) _defChain.get(index);
        l.clear();

        if(index < _useChain.size()) {
            l = (LinkedList) _useChain.get(index);
            if(l != null) {
                l.clear();
            }
        }
    }
    
    public void removeUseIndex(int index, VariableLocation loc) {
        LinkedList l = (LinkedList) _useChain.get(index);
        if(l == null) return;
        VariableLocation vl = null;
        for(int i = 0;i < l.size();i++) {
            vl = (VariableLocation) l.get(i);
            if(vl == loc) {
                l.remove(vl);
                return;
            }
        }
    }

    // remove o uso proveniente de um phi node.
    public void removePhiNodeUseIndex(AST label) {
        int index = SSA.getVarIndex(label.getText());
        LinkedList l = (LinkedList) _useChain.get(index);
        if(l == null) return;
        for(int i = 0;i < l.size();i++) {
            VariableLocation vl = (VariableLocation) l.get(i);
            if(vl.isPhiNode() && vl.getASTNode() == label) {
                l.remove(vl);
                return;
            }
        }
    }

    // remove qualquer use que seja nessa localizacao em particular
    /*public void removeUseLocation(FlowGraphNode node, AST ast) {
        for(int i = 0;i < _useChain.size();i++) {
            LinkedList ll = (LinkedList) _useChain.get(i);
            if(ll == null) continue;
            for(int j = 0;j < ll.size();j++) {
                VariableLocation loc = (VariableLocation) ll.get(j);
                if(loc.getFGNode() == node && loc.getASTNode() == ast) {
                    System.out.println("Removi " + loc);
                    ll.remove(loc);
                    return;
                }
            }
        }
    }*/
    
    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    ///////////////////////////////////////////////////////////////////
    ////                      private variables                    ////

    private ClassData _class;
    private STEntry _entry;

    // Listas contendo o local das definicoes e usos dessa variavel. Usado
    // durante a construcao do SSA. Depois de rodar os algoritmos de
    // simplificacao, essas listas ficam inconsistentes.
    private LinkedList _defSites;
    private LinkedList _useSites;

    private Stack _stack;
    private int _count;

    // Cada posicao e um indice dessa variavel. Cada 
    // posicao contem uma lista. A def pode ter ate 2, quando for
    // um comando ternario ?: . A use pode ter varios.
    private Vector _defChain;
    private Vector _useChain;
}
